package io.gitee.tziye.core.kv.model;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import io.gitee.tziye.RenaiClient;
import io.gitee.tziye.core.kv.PairOperator;
import io.gitee.tziye.core.kv.RenaiKey;
import io.gitee.tziye.enums.KvType;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;

@Slf4j
public class RenaiPair<T> extends RenaiKVBase<T> {

    private final PairOperator pairOperator;

    public RenaiPair(RenaiClient renaiClient, String key, T value, Class<T> clazz, Integer expireSeconds,
                     Integer localCacheSeconds) {
        super(renaiClient, key, clazz, KvType.Pair);
        initCache(localCacheSeconds);
        pairOperator = renaiClient.pair();
        if (expireSeconds == null) {
            set(value);
        } else {
            set(value, expireSeconds);
        }
    }

    private LoadingCache<String, T> localCache;

    private void initCache(Integer localCacheSeconds) {
        if (localCacheSeconds == null) {
            localCacheSeconds = 2;
        }
        localCache = CacheBuilder.newBuilder()
                .maximumSize(10000)
                .expireAfterWrite(localCacheSeconds, TimeUnit.SECONDS)
                .build(new CacheLoader<>() {
                    @Override
                    public T load(String key) {
                        return pairOperator.get(key, clazz);
                    }
                });
    }

    @Override
    public RenaiKey key() {
        return pairOperator.key(key);
    }

    @Override
    public void expire(int expireSeconds) {
        pairOperator.expire(key, expireSeconds);
    }

    @Override
    public boolean exists() {
        return pairOperator.exists(key);
    }

    @Override
    public boolean delete() {
        return pairOperator.delete(key);
    }

    public void set(T value) {
        pairOperator.set(key, value);
        localCache.put(key, value);
    }

    public void set(T value, int expireSeconds) {
        pairOperator.set(key, value, expireSeconds);
        localCache.put(key, value);
    }

    public T get() {
        try {
            return localCache.get(key);
        } catch (Exception e) {
            log.debug(e.getMessage(), e);
            T value = pairOperator.get(key, clazz);
            if (value != null) {
                localCache.put(key, value);
            }
            return value;
        }
    }

    public long incr(long num) {
        long v = pairOperator.incr(key, num);
        refresh();
        return v;
    }

    private void refresh() {
        try {
            localCache.refresh(key);
        } catch (Exception e) {
            log.debug(e.getMessage(), e);
        }
    }

}
